#region Imports

using System;
using System.Data;
using System.Data.SqlClient;
using PHSRAG.Insight21.CDS;

using BO = PHSRAG.Insight21.BusinessObjects;
using DataAccess = PHSRAG.Utility.DataAccess;
using ICachePolicy = PHSRAG.Insight21.Policy.ICachePolicy;

#endregion

namespace PHSRAG.Insight21.Humans
{
	/// <summary>
	/// Represents the binding between a person who is staffed to a study and the protocol
	/// </summary>
	public class StaffPerson: BaseStudyPerson, ICloneable
	{
		#region Constants

		private const string StaffPersonKey = "StaffPerson_9572FCDE-47B6-4e7b-921E-D32AE6F02D45";
		private const string StaffPersonTableName = "StaffPerson";
		public const char DELIMITER = ',';

		#endregion

		#region Private Instance Variables
		private bool isCurrent;
		private int roleId;
		private int addedPPId;
		private int removedPPId;
		private int organizationId;
		private string role;
		private string orgName;
		private Document consentForm;
		private string rexRoleName;
		private string processName;
		private string processType;
		private string status;
        private string moduleType;
		#endregion

		#region Public Properties
		/// <summary>
		/// Cache Key uniquely identifies this object within the cache.
		/// </summary>
		/// <returns>the unique string for this object.</returns>
		public override string CacheKey()
		{
			return StaffPersonKey;
		}

		/// <summary>
		/// Role name of the person on the study
		/// </summary>
		public string Role
		{
			get{ return role; }
			set
			{ 
				SetDirty(ref role, value);
                string key = InsightUtilities.Constants.HumansRolesKey;

                if (this.ModuleType == Humans.Constants.ModuleType.Animals)
                {
                    key = InsightUtilities.Constants.AnimalsRolesKey;
                }

                if (CachePolicy.GetCachedObject(key) != null)
				{                        
					DataRow roleMapping =
                        (((DataTable)CachePolicy.GetCachedObject(key)).
						Select(String.Format("Name = '{0}'", role)))[0];
					roleId = (int)roleMapping["Id"];
					rexRoleName = (string)roleMapping["RexName"];
				}
			}
		}

		/// <summary>
		/// Id of the role of the person on the study (for the given association)
		/// </summary>
		public int RoleId
		{
			get{ return roleId; }
			set{ 
				SetDirty(ref roleId, value);

                string key = InsightUtilities.Constants.HumansRolesKey;

                if (this.ModuleType == Humans.Constants.ModuleType.Animals)
                {
                    key = InsightUtilities.Constants.AnimalsRolesKey;
                }

				if (CachePolicy.GetCachedObject(key) != null)
                {
                    DataRow roleMapping = 
						(((DataTable)CachePolicy.GetCachedObject(key)).
						Select(String.Format("Id = '{0}'", roleId)))[0];
					role = (string)roleMapping["Name"];
					rexRoleName = (string)roleMapping["RexName"];
				}
			}
		}

		/// <summary>
		/// FwkDomainOrganizationId under which the person is staffed for the given role
		/// </summary>
		public int OrganizationId
		{
			get { return organizationId; }
			set { SetDirty(ref organizationId, value); }
		}

		/// <summary>
		/// Name of the organization under which the person is staffed for the given role
		/// </summary>
		public string OrganizationName
		{
			get { return orgName; }
			set { SetDirty(ref orgName, value); }
		}

        /// <summary>
        /// Gets whether the person has been through orientation training.  For eIACUC currently.
        /// Used during binding the staff and access pages.
        /// </summary>
        public bool IsOrientated
        {
            get
            {
                DataRow[] orientationData = (Person.Certifications.GetDataContainer().Tables[0].Select(
                    @"(SubType = 'Orientation' and Status = 'Pass')", "Date"));

                return (orientationData.Length > 0);
            }
        }

        /// <summary>
        /// Gets whether the person has been through OCC Health training for NHP (monkeys)
        /// within the last six months
        /// </summary>
        public bool IsOCCHealthCertified
        {
            get
            {

                DataRow[] occHealthData = (Person.Certifications.GetDataContainer().Tables[0].Select(
                    @"(SubType = 'Occ Health Clearance- NHP' and Status = 'Pass')", "Date DESC"));

                bool isCertified;
                if (occHealthData.Length > 0)
                {
                    DateTime certDate = (DateTime)occHealthData[0]["Date"];
                    if (certDate == DateTime.MinValue)
                        isCertified = false;
                    else
                    {
                        isCertified = (certDate > DateTime.Now.AddMonths(-6));
                    }
                }
                else
                    isCertified = false;

                return isCertified;
            }
        }

        /// <summary>
		/// Get whether the person has passed citi training and is certified (available here for binding
		/// purposes)
		/// </summary>
		public bool CitiCertified
		{
			get{ return Person.IsCitiCertified; }
		}

		/// <summary>
		/// Gets the date the person became citi certified (if the person passed training)
		/// (available here for binding purposes)
		/// </summary>
		public string CertificationDate
		{
			get{ return Person.CitiCertDate; }
		}

		/// <summary>
		/// Id of Protocol Process during which the staffperson got/is getting added.
		/// </summary>
		public int AddedProtocolProcessId
		{
			get{ return addedPPId; }
			set{ SetDirty(ref addedPPId, value); }
		}

		/// <summary>
		/// Id of Protocol Process during which the staffperson got/is getting removed.
		/// </summary>
		public int RemovedProtocolProcessId
		{
			get{ return removedPPId; }
			set{ SetDirty(ref removedPPId, value); }			
		}

		/// <summary>
		/// The RexRole Name used when moving the person over to Rex during the Submit method.
		/// </summary>
		public string RexRoleName
		{
			get { return rexRoleName; }
			set{ SetDirty(ref rexRoleName, value); }			
		}

		/// <summary>
		/// Whether the StaffPerson is currently staffed to the protocol
		/// </summary>
		public bool IsCurrent
		{
			get{ return isCurrent; }
			set{ SetDirty(ref isCurrent, value); }			
		}
		/// <summary>
		/// Gets the Institutional name
		/// </summary>
		public string InstitutionalName
		{
			get{ 
					if (orgName != string.Empty)
					{	string[] staffOrganizationName=orgName.ToString().Split('>');
						if(staffOrganizationName.Length>1)
						{return staffOrganizationName[1].Trim();}
						else
						{return string.Empty;}
					}
					else
					{return string.Empty;}
				}
		}

		/// <summary>
		/// Gets the document representing the Consent Form 
		/// </summary>
		public Document ConsentForm
		{
			get { return consentForm; }
		}


		/// <summary>
		/// Gets or sets the name, if any, of the process that added the staff person
		/// </summary>
		public string ProcessName
		{
			get { return processName; }
			set { processName = value; }
		}


		/// <summary>
		/// Gets or sets the type of the process that added the staff process
		/// </summary>
		public string ProcessType
		{
			get { return processType; }
			set { processType = value; }
		}

		/// <summary>
		/// Gets or sets the status (approved/pending) of the staff
		/// </summary>
		public string Status
		{
			get { return status; }
			set { status = value; }
		}


        /// <summary>
        /// The Module type (Animals or Humans)
        /// </summary>
        public string ModuleType
        {
            get { return moduleType; }
            set { moduleType = value; }
        }

		/// <summary>
		/// Used for display purposes - [FullName - Role]
		/// </summary>
		public string FullNameAndRole
		{
			get { return string.Format("{0} - {1}", FullName, Role); }
		}
		#endregion

        /// <summary>
		/// Using the input dataRow, picks off the relevant columns and uses them to set
		/// instance variables
		/// </summary>
		/// <param name="cachePolicy">policy to use to cache this instance</param>
		/// <param name="row">row with data to set the state of the StaffPerson</param>
		public StaffPerson(ICachePolicy cachePolicy, DataRow row) : base(cachePolicy, row)
		{
			object o = null;
            if (row.Table.Columns.Contains("ModuleType"))
            {
                o = row["ModuleType"];
                moduleType = (o == DBNull.Value ) ? string.Empty: (string)o;
            }
            o = row["Role"];
			Role = (o == DBNull.Value ) ? string.Empty : (string)o;
			o = row["RoleID"];
			RoleId = (o == DBNull.Value) ? 0 : (int)o;
			IsCurrent = (bool)row["IsCurrent"];
			o = row["OrganizationId"];
			OrganizationId = (o == DBNull.Value) ? 0 : (int)o;
			o = row["OrganizationName"];
			OrganizationName = (o == DBNull.Value ) ? string.Empty : (string)o;
			o = row["AddedProtocolProcessId"];
			addedPPId = (o == DBNull.Value ) ? 0: (int)o;
			if (row.Table.Columns.Contains("RemovedProtocolProcessId"))
			{
				o = row["RemovedProtocolProcessId"];
				removedPPId = (o == DBNull.Value ) ? 0: (int)o;
			}
			o = row["DocumentId"];
			if (o != DBNull.Value)
			{
				consentForm = new Document(CachePolicy);
				consentForm.Id = (int)o;
			}
			o = row["RexName"];
			rexRoleName = (o == DBNull.Value ) ? string.Empty : (string)o;
			if (row.Table.Columns.Contains("ProcessName"))
			{
				o = row["ProcessName"];
				processName = (o == DBNull.Value ) ? string.Empty: (string)o;
			}

			if (row.Table.Columns.Contains("Status"))
			{
				o = row["Status"];
				status = (o == DBNull.Value) ? string.Empty : (string)o;
			}

			if (row.Table.Columns.Contains("ProcessType"))
			{
				o = row["ProcessType"];
				processType = (o == DBNull.Value) ? string.Empty: (string)o;
			}

		}

		/// <summary>
		/// NEW object constructor; initializes the base with the specified cache policy
		/// </summary>
		/// <param name="cachePolicy">policy to use to cache this instance</param>
		public StaffPerson(ICachePolicy cachePolicy, int defaultPermissionLevel) : base(cachePolicy)
		{
            PermissionLevel = defaultPermissionLevel;
		}

		/// <summary>
		/// Update the object and set its Id to the mapId in the database
		/// </summary>
		/// <param name="transaction">transaction to use to perform saves</param>
		protected override void SaveObject(SqlTransaction transaction)
		{
			try
			{
				// save the person first so the admPersonId is accessible if the person
				// doesn't exist yet
				Person.Save(transaction);
				AdmPersonId = Person.Id;

				if (Dirty)
				{
					// UpdateStaffPerson returns more than one result set, so 
					// ExecuteScalar can't be used (since the Id is in the second table
					DataSet results =  DataAccess.GetDataSet(
						transaction.Connection,
						transaction,
						"UpdateStaffPerson", 
						DataAccess.BuildSqlParameterArray(
						"@protocolId", SqlDbType.Int, ProtocolId,
						"@admPersonId", SqlDbType.Int, Person.Id,
						"@mapId", SqlDbType.Int, Id,
						"@admPersonType", SqlDbType.Int, Person.AdmPersonType,
						"@accessRightBitValue", SqlDbType.Int, PermissionLevel,
						"@contactPerson", SqlDbType.Bit, IsContactPerson, 
						"@isCurrent", SqlDbType.Bit, (isCurrent) ? 1 : 0, 
						"@role", SqlDbType.Int, (roleId > 0) ? (object)roleId : (object)DBNull.Value,
						"@addedPPId", SqlDbType.Int, (addedPPId > 0) ? (object)addedPPId : (object)DBNull.Value, 
						"@removedPPId", SqlDbType.Int, (removedPPId > 0) ? (object)removedPPId : (object)DBNull.Value,
						"@organizationId", SqlDbType.Int, organizationId,
                        "@moduleType", SqlDbType.Char, moduleType));
					Id = (results.Tables.Count == 2) ? (int) results.Tables[1].Rows[0][0] : 
						(int) results.Tables[0].Rows[0][0];
				}
			}
			catch(Exception e)
			{
				Utility.ExceptionReport.WriteToEventLog(
					"Save of StaffPerson failed", e, true, Utility.ExceptionReport.ReportType.Error);
				throw new Exception("Save of StaffPerson failed", e);
			}
		}

		/// <summary>
		/// Removes the relationship captured by this instance from the database
		/// </summary>
		/// <param name="connectionString">connection string to database</param>
		/// <param name="sqlTransaction">transaction to use while deleting</param>
		protected override void DeleteObject( SqlTransaction sqlTransaction)
		{
			try
			{
				DataAccess.ExecuteNonQuery(
					sqlTransaction.Connection,
					sqlTransaction,
					"RemoveStudyStaff",
					DataAccess.BuildSqlParameterArray(
					"@protocolId", SqlDbType.Int, ProtocolId,
					"@admPersonId", SqlDbType.Int, Person.Id,
					"@mapId", SqlDbType.Int, Id,
					"@admPersonType", SqlDbType.Int, Person.AdmPersonType,
					"@removedPPId", SqlDbType.Int, removedPPId));
			}
			catch(Exception e)
			{
				Utility.ExceptionReport.WriteToEventLog(
					"Removal of StaffPerson failed", e, true, Utility.ExceptionReport.ReportType.Error);
				throw new Exception("Removal of StaffPerson failed", e);
			}
		}

		/// <summary>
		/// TODO: Not implemented
		/// </summary>
		/// <param name="connectionString"></param>
		/// <param name="args"></param>
		public override void Load(string connectionString, params object[] args)
		{
			throw new Exception("Not implemented");
		}

		/// <summary>
		/// Cache this instance using the prevalent cache policy.
		/// </summary>
		public override void Cache()
		{
			CachePolicy.Cache(StaffPersonKey, this);
		}

		/// <summary>
		/// Remove this instance from the cache using the prevalent cache policy.
		/// </summary>
		public override void UnCache()
		{
			CachePolicy.UnCache(StaffPersonKey);
		}

        object ICloneable.Clone()
        {
            return Clone();
        }

        public StaffPerson Clone()
        {
            StaffPerson clone = (StaffPerson)MemberwiseClone();
            clone.Id = 0;
            clone.IsClone = true;
            clone.Dirty = true;
            return clone;
        }

		public void UpdateCertificationId(int consentFormId)
		{
			try
			{
				int staffRoleType = 0;
				string exclusiveCertificationRoleTypes = string.Format("{0}{1}{2}",
														Constants.StaffRoleType.PrincipalInvestigator.ToString("d"),
														DELIMITER,
														Constants.StaffRoleType.SiteResponsibleInvestigator.ToString("d"));
				if (RoleId == (int)Constants.StaffRoleType.PrincipalInvestigator ||
					RoleId == (int)Constants.StaffRoleType.SiteResponsibleInvestigator)
					staffRoleType = RoleId;
				Utility.DataAccess.ExecuteNonQuery(Utility.DataAccess.ConnectionString, "UpdateStaffCertification", Utility.DataAccess.BuildSqlParameterArray(
					"@protocolId", SqlDbType.Int, ProtocolId,
					"@protocolStaffTypeId", SqlDbType.Int, staffRoleType,
					"@exclusiveCertificationRoleTypes", SqlDbType.VarChar, exclusiveCertificationRoleTypes,
					"@delimiter", SqlDbType.Char, DELIMITER,
					"@admPersonId", SqlDbType.Int, AdmPersonId,
					"@documentId", SqlDbType.Int, consentFormId));

				//set the property on the current staff
				consentForm = new Document(CachePolicy);
				consentForm.Id = consentFormId;
			}
			catch (Exception e)
			{
				Utility.ExceptionReport.WriteToEventLog(
						"Could not update certification id for the staff", e, true, Utility.ExceptionReport.ReportType.Error);
				throw new Exception("Could not update certification id for the staff", e);
			}
		}
	}
}
